/*
 * Class representing a subscriber of a library
 * A subscriber knows his first and last name,
 * his born date and his number in the library
 * @author M.T. Segarra
 * @version 0.0.1
 */
package subscribersManagement;


import java.util.*;

import loansManagement.Validator;
import loansManagement.Loan;
import main.Constraints;
import booksManagement.Book;
import dvdsManagement.Dvd;
import outils.StringCalendar;
import exceptions.*;

import javax.persistence.*;

import subscribersManagement.Entitled;

/**
 * Entity implementation class for Entity: Subscriber
 *
 */
@Entity
@Inheritance (strategy=InheritanceType.JOINED)
@Table(name="subscribers")
public class Subscriber {
	
	/**
	 * @uml.property  name="numberCreated"
	 */
	private static long numberCreated;
	
	/**
	 * @uml.property  name="number"
	 */
	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	@Column(name="id_subscriber",unique = true,nullable=false)
	private long number;

	/**
	 * @uml.property name="firstName"
	 */
	private String firstName;

	/**
	 * @uml.property name="lastName"
	 */
	private String lastName;

	/**
	 * @uml.property name="bornDate"
	 */
	@Transient
	private Calendar bornDate;
	
	private Calendar subscriptionDate;
	
	/**
	 * @uml.property  name="numberOfBookLoan"
	 */
	private int numberOfBookLoan =0;
	
	/**
	 * @uml.property  name="numberOfDvdLoan"
	 */
	private int numberOfDvdLoan =0;
	
	/**
	 * @uml.property   name="currentloans"
	 * @uml.associationEnd   multiplicity="(0 -1)" ordering="true" inverse="suscriber:loansManagement.Loan"
	 */
	@OneToMany(mappedBy="pk.subscriber",fetch=FetchType.EAGER,cascade=CascadeType.ALL)
	private Set<Loan> currentloans ;
	
	/**
	 * @uml.property   name="loans"
	 * @uml.associationEnd   multiplicity="(0 -1)" ordering="true" aggregation="shared" inverse="suscriber1:loansManagement.Loan"
	 */
	@Transient
	private Set<Loan> loans ;

	/**
	 * @uml.property  name="entitles"
	 * @uml.associationEnd  multiplicity="(0 -1)" inverse="mainSubscriber:subscribersManagement.Entitled"
	 * @uml.association  name="subscriberEntitled"
	 */
	@OneToMany(mappedBy = "mainSubscriber")
	private Set<Entitled> entitles;

	/**
	 * Getter of the property <tt>entitles</tt>
	 * @return  Returns the entitles.
	 * @uml.property  name="entitles"
	 */
	public Set<Entitled> getEntitles() {
		return entitles;
	}

	/**
	 * Setter of the property <tt>entitles</tt>
	 * @param entitles  The entitles to set.
	 * @uml.property  name="entitles"
	 */
	public void setEntitles(Set<Entitled> entitles) {
		this.entitles = entitles;
	}
	
	/**
	 * Getter of the property <tt>number</tt>
	 * 
	 * @return Returns the number.
	 * @uml.property name="number"
	 */
	public long getNumber() {
		return number;
	}

	/**
	 * Setter of the property <tt>number</tt>
	 * 
	 * @param number
	 *            The number to set.
	 * @uml.property name="number"
	 */
	public void setNumber(long number) {
		this.number = number;
	}

	public static long getNumberCreated() {
		return numberCreated;
	}

	public static void setNumberCreated(long numberCreated) {
		Subscriber.numberCreated = numberCreated;
	}

	public Calendar getSubscriptionDate() {
		return subscriptionDate;
	}

	public void setSubscriptionDate(Calendar subscriptionDate) {
		this.subscriptionDate = subscriptionDate;
	}

	public int getNumberOfBookLoan() {
		return numberOfBookLoan;
	}

	public void setNumberOfBookLoan(int numberOfBookLoan) {
		this.numberOfBookLoan = numberOfBookLoan;
	}

	public int getNumberOfDvdLoan() {
		return numberOfDvdLoan;
	}

	public void setNumberOfDvdLoan(int numberOfDvdLoan) {
		this.numberOfDvdLoan = numberOfDvdLoan;
	}

	public Set<Loan> getCurrentloans() {
		return currentloans;
	}

	public void setCurrentloans(Set<Loan> currentloans) {
		this.currentloans = currentloans;
	}

	public Set<Loan> getLoans() {
		return loans;
	}

	public void setLoans(Set<Loan> loans) {
		this.loans = loans;
	}

	/**
	 * Getter of the property <tt>firstName</tt>
	 * 
	 * @return Returns the firstName.
	 * @uml.property name="firstName"
	 */
	public String getFirstName() {
		return firstName;
	}

	/**
	 * Setter of the property <tt>firstName</tt>
	 * 
	 * @param firstName
	 *            The firstName to set.
	 * @uml.property name="firstName"
	 */
	public void setFirstName(String firstName) {
		this.firstName = firstName;
	}

	/**
	 * Getter of the property <tt>lastname</tt>
	 * 
	 * @return Returns the lastname.
	 * @uml.property name="lastName"
	 */
	public String getLastName() {
		return lastName;
	}

	/**
	 * Setter of the property <tt>lastname</tt>
	 * 
	 * @param lastname
	 *            The lastname to set.
	 * @uml.property name="lastName"
	 */
	public void setLastName(String lastName) {
		this.lastName = lastName;
	}

	/**
	 * Getter of the property <tt>bornDate</tt>
	 * 
	 * @return Returns the bornDate.
	 * @uml.property name="bornDate"
	 */
	public Calendar getBornDate() {
		return bornDate;
	}

	/**
	 * Setter of the property <tt>bornDate</tt>
	 * 
	 * @param bornDate
	 *            The bornDate to set.
	 * @uml.property name="bornDate"
	 */
	public void setBornDate(Calendar bornDate) {
		this.bornDate = bornDate;
	}
	
	public void addEntitled(Entitled entitled) {
		this.entitles.add(entitled);
	}

	public void removeEntitled(Entitled entitled) {
		this.entitles.remove(entitled);
	}
	
	public Subscriber() {
	}

	public Subscriber(String firstName, String lastName, Calendar bornDate, Calendar subscriptionDate) {
		this.firstName=firstName;
		this.lastName=lastName;
		this.bornDate=bornDate;
		this.subscriptionDate=subscriptionDate;
		this.entitles= new HashSet<Entitled>();
		this.currentloans= new HashSet<Loan>();
	}
	
	
	/**
	 * Decides if the subscriber object (parameter) is the same subscriber as
	 * this one Same first name, last name, and born date
	 * 
	 * @param subscriber object to be compared with this one
	 * 
	 * @return true if parameter object and this one are equal
	 * 
	 * @return false if parameter object is null or different from this one
	 */
	@Override
	public boolean equals(Object subscriber) {
		if (subscriber == null)
			return false;
		Subscriber s = (Subscriber) subscriber;

		if (s.number == number)
			return true;

		if ((s.firstName == null) || (s.lastName == null)
				|| (s.bornDate == null))
			return false;

		if ((this.firstName == null) || (this.lastName == null)
				|| (this.bornDate == null))
			return false;

		boolean res = (s.firstName.equals(firstName))
				&& (s.lastName.equals(lastName))
				&& (s.bornDate.equals(bornDate));

		return res;
	}
	
	public void lend(Loan loan) throws BadParametersException, LentMediaException, ChildLoanException {
		if (loan == null)
			throw new BadParametersException();
         
		if (!(Validator.canLend(this, loan.getMedia())))
			throw new LentMediaException("Impossible d'emprunter ce media");
		
		this.currentloans.add(loan);
         if (loan.getMedia() instanceof Book){
        	 numberOfBookLoan++;
         }
         if (loan.getMedia() instanceof Dvd){
        	 numberOfDvdLoan++;
         }
	}
	
//	
//	public int nbBooks(){
//		StringCalendar sc=new StringCalendar();
//		Calendar c = Calendar.getInstance();
//		int yearNow = c.get(Calendar.YEAR);
//		int yearSubscription= sc.stringToCalendar(this.subscriptionDate).get(Calendar.YEAR);
//		int yearBorn = sc.stringToCalendar(this.bornDate).get(Calendar.YEAR);
//		
//		if(yearNow-yearBorn<18){
//			return 5;
//		}
//		else if(yearNow-yearSubscription<1){
//			return 4;
//		}
//		else if(yearNow-yearSubscription<2){
//			return 5;
//		}
//		else return 7;
//	}
//	
//	public int nbDvds(){
//		StringCalendar sc=new StringCalendar();
//		Calendar c = Calendar.getInstance();
//		int yearNow = c.get(Calendar.YEAR);
//		int yearSubscription= sc.stringToCalendar(this.subscriptionDate).get(Calendar.YEAR);
//		int yearBorn = sc.stringToCalendar(this.bornDate).get(Calendar.YEAR);
//		
//		if(yearNow-yearBorn<18){
//			return 0;
//		}
//		else if(yearNow-yearSubscription<1){
//			return 2;
//		}
//		else if(yearNow-yearSubscription<2){
//			return 3;
//		}
//		else return 5;
//	}

	/**
	 * Add a new loan to the subscriber if quota ok
	 * 
	 * @param loan
	 *            the loan to add to the subscriber
	 * @throws TooManyLoansException
	 * @throws BadParametersException
	 */
//	public void lendBook(Loan specBook) throws TooManyLoansException,
//			BadParametersException {
//		if (specBook == null)
//			throw new BadParametersException();
//
//		if (!canLendBook())
//			throw new TooManyLoansException();
//
//		this.specimens.add(specBook);
//	}

	/**
	 * @return true if the subscriber can lend books
	 */
//	public boolean canLendBook() {
//		int specBook=0;
//		for (Loan specimen : this.specimens) {
//			if(specimen.getMedia().getType()=="Book")
//				specBook++;
//		}
//		return specBook < this.nbBooks() ;
//	}

	/**
	 * Add a new loan to the subscriber if quota ok
	 * 
	 * @param loan
	 *            the loan to add to the subscriber
	 * @throws TooManyLoansException
	 * @throws BadParametersException
	 */
//	public void lendDvd(Loan specDvd) throws TooManyLoansException,
//			BadParametersException {
//		if (specDvd == null)
//			throw new BadParametersException();
//
//		if (!canLendBook())
//			throw new TooManyLoansException();
//
//		this.specimens.add(specDvd);
//	}

	/**
	 * @return true if the subscriber can lend books
	 */
//	public boolean canLendDvd() {
//		int specDvd=0;
//		for (Loan specimen : this.specimens) {
//			if(specimen.getMedia().getType()=="Dvd")
//				specDvd++;
//		}
//		return specDvd < this.nbDvds() ;
//	}
	
	/**
	 * Remove the loan in parameter from the list of current loans of the
	 * subscriber
	 * 
	 * @param loan
	 *            the loan to be finished
	 * @throws BadParametersException
	 * @throws LentBookException
	 *             if loan not found in the list
	 */
//	public void returnMedia(Loan specimen) throws BadParametersException,
//			LentMediaException {
//		if (specimen == null)
//			throw new BadParametersException();
//
//		if (!this.specimens.remove(specimen))
//			throw new LentMediaException("The lender does not contain the loan");
//	}

	/*
	 * Returns a description of the subscriber
	 */
	@Override
	public String toString() {
		return this.getNumber() + " " + this.firstName + ", " + this.lastName;
	}
	
}
